/*
 * spi_lcd.c
 *
 *  Created on: 2012-9-11
 *      Author: Administrator
 */
#include "dev.h"
#include "gpio.h"
#include "kernel.h"
#include "iodev.h"
#include <string.h>
#include "ioctl.h"
#include "regs.h"
#include "config.h"
#include "running.h"

 
#define LCD_W CONFIG_LCD_W       /**< LCM LCD Width */
#define LCD_H CONFIG_LCD_H        /**< LCM LCD Height */
#define LCD_BUF_LEN ((LCD_W * LCD_H) / 8)       /**< LCD Framebuffer Length */
	 
 static uint8_t lcd_fb[LCD_BUF_LEN+1];	   /**< LCD Framebuffer */
 static lcd_inf_t * lcd_inf;			 /**< LCD Inferface */
 
 extern uint8_t lcd_init_cmd[]; 		 /**< LCD Initialization Commands */
 extern uint8_t lcd_x_ofs;				 /**< LCD x adjustment */
 extern uint8_t lcd_y_ofs;				 /**< LCD y adjustment */

  extern int lcd_disp_attribute(uint32_t cmd,void *arg);

 int lcd_write_reg(uint32_t reg)
{
	spi_slave_t * spi = &(lcd_inf->inf.spi_lcd.spi);
	 uint8_t cmd = reg&0xff;
	gpio_output(lcd_inf->inf.spi_lcd.GPIO_A0, 0); /* Command Mode */
	
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
	spi_xfer(spi, &cmd, 1, NULL, 0);
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END);


        return 0;
}

 int lcd_write_data(uint32_t x, uint32_t y, uint32_t data)
{
       	 spi_slave_t * spi = &(lcd_inf->inf.spi_lcd.spi);
	 uint32_t A0 = lcd_inf->inf.spi_lcd.GPIO_A0;
	 uint8_t cmd[3];
	 unsigned char start, end;
 
	 x += lcd_x_ofs;
	 y += lcd_y_ofs;
 
	 start = y >> 3;
	 
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);

	 gpio_output(A0, 0);
	 cmd[0] = 0xB0 | start;
	 cmd[1] = 0x10 + (x >> 4);
	 cmd[2] = (x & 0xf);
	 spi_xfer(spi, &cmd, 3, NULL, 0);
	 gpio_output(A0, 1);
	 spi_xfer(spi, data, 1, NULL, 0);

	 spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END);      
          
        return 0;
}
 
 static int st7567_update(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
	 spi_slave_t * spi = &(lcd_inf->inf.spi_lcd.spi);
	 uint32_t A0 = lcd_inf->inf.spi_lcd.GPIO_A0;
	 uint8_t cmd[3];
	 unsigned char start, end;
 
	 x += lcd_x_ofs;
	 y += lcd_y_ofs;
 
	 start = y >> 3;
	 end = ((y + h + 7) >> 3);
	 
		 spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
 //   printf("%d:%d w=%d--h=%d\n",start,end,w,h);
	  
	 while (start < end) {
		 gpio_output(A0, 0);
			 cmd[0] = 0xB0 | start;
		 cmd[1] = 0x10 + (x >> 4);
		 cmd[2] = (x & 0xf);
		 spi_xfer(spi, &cmd, 3, NULL, 0);
		 gpio_output(A0, 1);
		 spi_xfer(spi, lcd_fb + LCD_W*start+x, w, NULL, 0);
		 start ++;
	 }	 
	 spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END); 
//  	putsdex("start0:",lcd_fb,w);
	 
 //  printf("%d:%d %p---%p\n",start,end,lcd_fb,spi);
		 return 0;
 }

#define  TFT177

#ifdef TFT177
#define SIZE_ARRAY(x)	(sizeof(x)/sizeof(x[0]))

// reg map
#define REG_SLEEP_OUT	0x11
#define REG_GAMMA_SET	0x26


/*
	reg	: ic register
	data	: data buf
	dlen	: data buf lenth
*/
int lcd_data_wtrite(uint8_t reg, uint8_t *data, uint8_t dlen, uint8_t delayms)
{
	spi_slave_t * spi = &(lcd_inf->inf.spi_lcd.spi);
	 uint8_t cmd = reg&0xff;
	 
	gpio_output(lcd_inf->inf.spi_lcd.GPIO_A0, 0); /* Command Mode */
	
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
	spi_xfer(spi, &cmd, 1, NULL, 0);
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END);

#if 0
	if(delayms)
	{
		mdelay(delayms);
	}

	// step 2. data mode, write data 
 	if(dlen)	 
 	{
		gpio_output(A0, 1);
		spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
		for(nr=0; nr<dlen; nr++)
		{
			spi_xfer(spi, data[nr]&0xFF, 1, NULL, 0);
		}
		spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END); 
	}
#endif
	return 0;
}

int lcd_data_read(uint8_t reg, uint8_t *data, uint8_t dlen)
{
	spi_slave_t * spi = &(lcd_inf->inf.spi_lcd.spi);
	uint32_t A0 = lcd_inf->inf.spi_lcd.GPIO_A0;
	uint8_t nr;

	// step 1. cmd mode, write reg 
	gpio_output(A0, 0);
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
	
	spi_xfer(spi, reg&0xFF, 1, NULL, 0);
	//gpio_output(A0, 1);
	
	spi_xfer(spi, NULL, dlen, data, 0);
	
	spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END); 


	return 0;
}

#endif

 
static  int st7567_init(iodev_t * dev)
 {
		 dev_inf_t * dinf = iodev_get_inf(dev);
		 if (dinf == NULL) return -1;
 
		 lcd_inf_t * inf = (lcd_inf_t *)dinf->priv;
		 spi_slave_t * spi = &(inf->inf.spi_lcd.spi);
		 int i = 0;
		 uint8_t cmd;
		 spi_param_t param;

		 param.baud = 5000000; /* max clock */
		 param.bus_16bit = 0;
		 param.cpha = 1;
		 param.cpol = 1;
		 param.lsb_mod = 0;
		 
		 spi_register_slave(spi, &param);
 
		 if (inf->backlight_ctl) inf->backlight_ctl(1); /* Backlight ON */

 #ifdef TFT177
 		uint8_t wbuf[32];	
 						
		//lcd_data_wtrite(REG_SLEEP_OUT, NULL, 0, 120);
		lcd_write_reg(REG_SLEEP_OUT);

		wbuf[0] = 0x04;
		//lcd_data_wtrite(REG_GAMMA_SET, wbuf, 1, 0);
		

		//lcd_data_wtrite(0x29, NULL, 0, 0);	// Display On
		
 #else
		 gpio_output(inf->inf.spi_lcd.GPIO_A0, 0); /* Command Mode */
 
		 spi_xfer(spi, NULL, 0, NULL, SPI_XFER_BEGIN);
 
		 while ((cmd = lcd_init_cmd[i ++])) {
				 if (cmd == 0xFF) mdelay(lcd_init_cmd[i ++]); /* Delay Required */
				 else spi_xfer(spi, &cmd, 1, NULL, 0);
		 }
		 spi_xfer(spi, NULL, 0, NULL, SPI_XFER_END);
#endif		 
		 memset(lcd_fb,0x00, LCD_BUF_LEN);
		  lcd_inf = inf;
		 return 0;
 }

 
 static int st7567_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
 {
		 switch (cmd) {
		 case IOCTL_LCD_GET_INFO:
				 {
						 lcd_info_t * p = (lcd_info_t *)arg;
						 if (p) {
								 p->width = LCD_W;
								 p->height = LCD_H;
								 p->fbuf = lcd_fb;
								 p->fbuf_len = LCD_BUF_LEN;
						 } 
				 }
				 break;
		 case IOCTL_LCD_UPDATE:
				 {
                   lcd_block_t * blk = (lcd_block_t *)arg;
						 if (blk) {
								 st7567_update(blk->x, blk->y, blk->w, blk->h);
						 }
						 else {
								 st7567_update(0, 0, LCD_W, LCD_H);
						 }
				 }
				 break;
		
		case IOCTL_LCD_BL:
				{
					uint32_t onoff = (uint32_t)arg;
					if (lcd_inf->backlight_ctl) lcd_inf->backlight_ctl(onoff);
				}
				break;
		default:
			if((cmd&IOCTL_LCD_ATTRIBUTE) == IOCTL_LCD_ATTRIBUTE) {
					return lcd_disp_attribute(cmd&0xff,arg);
			} else {
				return -1;
			}
		}

		 return 0;
 }
 static int st7567_suspend(iodev_t *dev,int sleep)
 {
    if (sleep >= 2) {
        if (lcd_inf->backlight_ctl) lcd_inf->backlight_ctl(0);
    }
    return 0;
 }
static int st7567_wakeup(iodev_t *dev, int sleep)
{
    if (sleep >= 2) {
        if (lcd_inf->backlight_ctl) lcd_inf->backlight_ctl(1);
    }
    return 0;
}
 

 
 static const iodev_t lcd_st7567 __iodev_entry = {
		 .name = "lcd",
		 .init = st7567_init,
		 .read = NULL,
		 .write = NULL,
		 .ioctl = st7567_ioctl,
		 .poll = NULL,
#ifdef CONFIG_POWER_CTL
		 .suspend = st7567_suspend,
		 .wakeup = st7567_wakeup,
#endif
 };


